package cc.shacocloud.mirage.restful;

import cc.shacocloud.mirage.utils.ClassUtil;
import cc.shacocloud.mirage.utils.Utils;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 抽象的处理器方法映射
 *
 * @param <T> 映射信息泛型
 */
@Slf4j
public abstract class AbstractHandlerMethodMapping<T> extends AbstractApplicationContextAware {
    
    private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
    
    /**
     * 注册处理器方法
     *
     * @param handler 处理器对象
     * @param method  执行方法
     * @param mapping 映射信息
     */
    protected void registerHandlerMethod(Object handler, Method method, T mapping) {
        HandlerMethod handlerMethod = createHandlerMethod(handler, method);
        validateMethodMapping(handlerMethod, mapping);
        this.mappingLookup.put(mapping, handlerMethod);
        registerHandlerMethod(handlerMethod, mapping);
    }
    
    /**
     * 创建 {@link HandlerMethod} 实例
     *
     * @param handler Bean名称或实际处理程序实例
     * @param method  目标方法
     * @return 创建的 HandlerMethod
     */
    protected HandlerMethod createHandlerMethod(Object handler, Method method) {
        return new HandlerMethod(handler, method);
    }
    
    protected void validateMethodMapping(HandlerMethod handlerMethod, T mapping) {
        // 断言所提供的映射是唯一的
        HandlerMethod existingHandlerMethod = this.mappingLookup.get(mapping);
        if (existingHandlerMethod != null && !existingHandlerMethod.equals(handlerMethod)) {
            throw new IllegalStateException(
                    "无法映射 '" + Utils.methodDescription(handlerMethod.getBeanType(), handlerMethod.getMethod())
                            + "' 方法绑定的路径 " + mapping + "：已经映射到 '" +
                            Utils.methodDescription(existingHandlerMethod.getBeanType(), existingHandlerMethod.getMethod()) + "' 方法上");
        }
    }
    
    
    /**
     * 格式化映射，方便日志输出
     */
    protected String formatMappings(Class<?> userType, @NotNull Map<Method, T> methods) {
        String formattedType = Arrays.stream(ClassUtil.getPackageName(userType).split("\\."))
                .map(p -> p.substring(0, 1))
                .collect(Collectors.joining(".", "", "." + userType.getSimpleName()));
        Function<Method, String> methodFormatter = method -> Arrays.stream(method.getParameterTypes())
                .map(Class::getSimpleName)
                .collect(Collectors.joining(",", "(", ")"));
        return methods.entrySet().stream()
                .map(e -> {
                    Method method = e.getKey();
                    return e.getValue() + ": " + method.getName() + methodFormatter.apply(method);
                })
                .collect(Collectors.joining("\n\t", "\n\t" + formattedType + ": " + "\n\t", ""));
    }
    
    /**
     * 根据方法获取映射信息，如果返回 {@code null}则表示没有映射信息
     */
    @Nullable
    protected abstract T getMappingInfoForMethod(Method method, Class<?> handlerType);
    
    /**
     * 注册处理器方法
     *
     * @param handlerMethod {@link HandlerMethod}
     * @param mapping       映射信息
     */
    protected abstract void registerHandlerMethod(HandlerMethod handlerMethod, T mapping);
    
}
